function CalendarViewBody(dom) {
    this._dom = dom;
    this._defaultMode = new NullMode();
    this._currentMode = this._defaultMode;
    this._bindLocationBarEvents();
    this._preventSelection();
    this._locationBarContainer = $('#calendar-location');
    this._locationBar = null;
    this._defaultEffectMode = null;
    this._onDrawCallback = [];
}

CalendarViewBody.prototype = {
    onDraw: function(callback) {
        this._onDrawCallback.push(callback);
    },

    _onDraw: function(days) {
        this._onDrawCallback.foreach(function(callback) {
            callback(days);
        });
    },

    updateDays: function(days) {
        this._updateMonthTable(days);
        this._bindModeEvent();
        this._onDraw(days);
    },

    updateLocations: function() {
        var me = this;
        pwc.registerOneTimeFunction(me, function() { me._showLocations() });
    },

    render: function() {
        this._showMonthTable();
        this._showLocations();
        this._bindModeEvent();
        this._setCursor();
        this._onDraw();
    },

    setMonthModel: function(monthModel) {
        this._monthModel = monthModel;
    },

    setMode: function(mode) {
        this._currentMode = mode;
        this._setCursor();
    },

    setDefaultMode: function(mode) {
        this._defaultMode = mode;
    },

    restoreDefaultMode: function() {
        this._defaultEffectMode.clearSelection();
        this._currentMode = this._defaultMode;
        this._setCursor();
    },

    _updateMonthTable: function(days) {
        var me = this;
        days.foreach(function(day) {
            me._monthView.updateDay(day);
        });
    },

    _preventSelection: function() {
        this._dom.css('-moz-user-select', 'none');
        this._dom.css('-webkit-user-select', 'none');
        this._dom.bind('selectstart', function() {
            return false;
        });
    },

    _showMonthTable: function() {
        if (this._monthDom) {
            this._monthDom.remove();
        }

        this._monthView = new MonthView(this._monthModel);
        this._monthDom = this._monthView.render();
        this._dom.append(this._monthDom);
        pwc.scrollbar.refresh();
    },

    _showLocations: function() {
        if (this._locationBar) {
            this._locationBar.remove();
        }
        this._locationBar = new LocationBar(this._monthModel).render();
        this._locationBarContainer.append(this._locationBar);
    },

    _setCursor: function() {
        $('#calendar-container')
                .removeClass()
                .addClass(this._currentMode.cursor);
    },

    _bindLocationBarEvents: function() {
        var me = this;
        // Set timeout to 110ms to bind the action after pwc.scrollbar resize event.
        $(window).bind('resize', function() { me._locationRenderEventListener(); });
    },

    _locationRenderEventListener: function() {
        var me = this;
        setTimeout(function() {
            me._showLocations();
        }, 110);
    },

    _bindModeEvent: function() {
        var me = this;
        var dayDoms = $('#calendar-month-table tr td.current-month-day>div');
        var noteDoms = $('#calendar-month-table span.note');
        this._defaultEffectMode = new DefaultEffectMode();

        var wrapperDom = $('#calendar-body-matrix');
        wrapperDom.unbind();
        var calendarBodyEventWrapper = new CalendarBodyEventWrapper(wrapperDom, dayDoms, noteDoms);

        calendarBodyEventWrapper.onMouseenter(function(sender, e) {
            me._triggerEvent(sender, e, me._defaultEffectMode.onMouseenter);
        });
        calendarBodyEventWrapper.onMouseover(function(sender, e) {
            me._triggerEvent(sender, e, me._defaultEffectMode.onMouseover);
        });
        calendarBodyEventWrapper.onMouseout(function(sender, e) {
            me._triggerEvent(sender, e, me._defaultEffectMode.onMouseout);
        });
        calendarBodyEventWrapper.onMousedown(function(sender, e) {
            me._triggerEvent(sender, e, me._defaultEffectMode.onMousedown);
        });
        calendarBodyEventWrapper.onMouseup(function(sender, e) {
            me._triggerEvent(sender, e, me._defaultEffectMode.onMouseup);
        });
    },

    _triggerEvent: function(sender, e, eventCallback) {
        this._defaultEffectMode.setInnerMode(this._currentMode);
        eventCallback.call(this._defaultEffectMode, sender, e);
    }
};

CalendarViewBody.getMonthTitleOf = function(monthIndex) {
    var monthTitles = [
        i18n('LABEL_MONTH_JANUARY'),
        i18n('LABEL_MONTH_FEBRUARY'),
        i18n('LABEL_MONTH_MARCH'),
        i18n('LABEL_MONTH_APRIL'),
        i18n('LABEL_MONTH_MAY'),
        i18n('LABEL_MONTH_JUNE'),
        i18n('LABEL_MONTH_JULY'),
        i18n('LABEL_MONTH_AUGUST'),
        i18n('LABEL_MONTH_SEPTEMBER'),
        i18n('LABEL_MONTH_OCTOBER'),
        i18n('LABEL_MONTH_NOVEMBER'),
        i18n('LABEL_MONTH_DECEMBER')
    ];
    return monthTitles[monthIndex];
};
CalendarViewBody.getWeekShortTitles = function() {
    return [
        i18n('LABEL_WEEK_MON'),
        i18n('LABEL_WEEK_TUE'),
        i18n('LABEL_WEEK_WED'),
        i18n('LABEL_WEEK_THU'),
        i18n('LABEL_WEEK_FRI'),
        i18n('LABEL_WEEK_SAT'),
        i18n('LABEL_WEEK_SUN')
    ];
};